﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.IO;
using System.Reflection;
using System.Xml;
using HoNSharp;

namespace LibBot
{
    public class Core
    {
        private static HoNClient hon;

        private HoNEntityManager hemUsers = new HoNEntityManager();
        private HoNEntityManager hemChannels = new HoNEntityManager();

        public delegate void AcceptClanInvitationEventHandler(object sender, EventArgs e);
        public delegate void BuddyWhisperEventHandler(object sender, BuddyWhisperEventArgs e);
        public delegate void ChannelKickEventHandler(object sender, ChannelKickEventArgs e);
        public delegate void ChannelMessageEventHandler(object sender, ChannelMessageEventArgs e);
        public delegate void ClanInvitationEventHandler(object sender, ClanInvitationEventArgs e);
        public delegate void ClanMessageEventHandler(object sender, ClanMessageEventArgs e);
        public delegate void CommandEventHandler(object sender, CommandEventArgs e);
        public delegate void ConnectEventHandler(object sender, EventArgs e);
        public delegate void DeclineClanInvitationEventHandler(object sender, EventArgs e);
        public delegate void DiceRollEventHandler(object sender, DiceRollEventArgs e);
        public delegate void DisconnectEventHandler(object sender, DisconnectEventArgs e);
        public delegate void EmoteEventHandler(object sender, EmoteEventArgs e);
        public delegate void InitialStatusEventHandler(object sender, InitialStatusEventArgs e);
        public delegate void InstantMessageEventHandler(object sender, InstantMessageEventArgs e);
        public delegate void LeaveChannelEventHandler(object sender, LeaveChannelEventArgs e);
        public delegate void LoginCompleteEventHandler(object sender, LoginCompleteEventArgs e);
        public delegate void NameChangedEventHandler(object sender, NameChangedEventArgs e);
        public delegate void NewChannelEventHandler(object sender, NewChannelEventArgs e);
        public delegate void PlayerDemotedEventHandler(object sender, PlayerDemotedEventArgs e);
        public delegate void PlayerJoinedChannelEventHandler(object sender, PlayerJoinedChannelEventArgs e);
        public delegate void PlayerLeftChannelEventHandler(object sender, PlayerLeftChannelEventArgs e);
        public delegate void PlayerPromotedEventHandler(object sender, PlayerPromotedEventArgs e);
        public delegate void PluginConflictEventHandler(object sender, PluginConflictEventArgs e);
        public delegate void SendChannelMessageEventHandler(object sender, SendChannelMessageEventArgs e);
        public delegate void SendClanInviteEventHandler(object sender, SendClanInviteEventArgs e);
        public delegate void SendClanMessageEventHandler(object sender, SendClanMessageEventArgs e);
        public delegate void SendEmoteEventHandler(object sender, SendEmoteEventArgs e);
        public delegate void SendInstantMessageEventHandler(object sender, SendInstantMessageEventArgs e);
        public delegate void SendWhisperEventHandler(object sender, SendWhisperEventArgs e);
        public delegate void SilenceLiftedEventHandler(object sender, SilenceLiftedEventArgs e);
        public delegate void SilencePlacedEventHandler(object sender, SilencePlacedEventArgs e);
        public delegate void SlashCommandEventHandler(object sender, SlashCommandEventArgs e);
        public delegate void StatusUpdateEventHandler(object sender, StatusUpdateEventArgs e);
        public delegate void TopicChangedEventHandler(object sender, TopicChangedEventArgs e);
        public delegate void WhisperEventHandler(object sender, WhisperEventArgs e);
        public delegate void WhoisCompleteEventHandler(object sender, WhoisCompleteEventArgs e);

        #region Events
        /// <summary>
        /// Occurs when the bot accepts a clan invitation.
        /// </summary>
        public event AcceptClanInvitationEventHandler onAcceptClanInvitation;

        /// <summary>
        /// Occurs when a buddy sends a whisper to everyone on their buddy list.
        /// </summary>
        public event BuddyWhisperEventHandler onBuddyWhisper;

        /// <summary>
        /// Occurs when a player is kicked from a channel.
        /// </summary>
        public event ChannelKickEventHandler onChannelKick;

        /// <summary>
        /// Occurs when a player sends a message to a channel.
        /// </summary>
        public event ChannelMessageEventHandler onChannelMessage;

        /// <summary>
        /// Occurs when the bot receives a clan invitation from a player.
        /// </summary>
        public event ClanInvitationEventHandler onClanInvite;

        /// <summary>
        /// Occurs when a clan member sends a message to the rest of the clan.
        /// </summary>
        public event ClanMessageEventHandler onClanMessage;

        /// <summary>
        /// Occurs when a player tries to use a command.
        /// </summary>
        public event CommandEventHandler onCommand;

        /// <summary>
        /// Occurs when the bot connects to the chat server.
        /// </summary>
        public event ConnectEventHandler onConnect;

        /// <summary>
        /// Occurs when the bot declines a clan invitation.
        /// </summary>
        public event DeclineClanInvitationEventHandler onDeclineClanInvitation;

        /// <summary>
        /// Occurs when a player rolls dice in a channel.
        /// </summary>
        public event DiceRollEventHandler onDiceRoll;

        /// <summary>
        /// Occurs when the bot disconnects from the chat server.
        /// </summary>
        public event DisconnectEventHandler onDisconnect;

        /// <summary>
        /// Occurs when a player sends an emote to a channel.
        /// </summary>
        public event EmoteEventHandler onEmote;

        /// <summary>
        /// Occurs when the bot receives the current statuses right after logging in.
        /// </summary>
        public event InitialStatusEventHandler onInitialStatus;

        /// <summary>
        /// Occurs when the bot receives an instant message from a player.
        /// </summary>
        public event InstantMessageEventHandler onInstantMessage;

        /// <summary>
        /// Occurs when the bot leaves a channel.
        /// </summary>
        public event LeaveChannelEventHandler onLeaveChannel;

        /// <summary>
        /// Occurs when the bot completes the login process.
        /// </summary>
        public event LoginCompleteEventHandler onLoginComplete;

        /// <summary>
        /// Occurs when a player's name changes in a channel.
        /// </summary>
        public event NameChangedEventHandler onNameChanged;

        /// <summary>
        /// Occurs when the bot joins a new channel.
        /// </summary>
        public event NewChannelEventHandler onNewChannel;

        /// <summary>
        /// Occurs when a player is demoted in rank of a channel.
        /// </summary>
        public event PlayerDemotedEventHandler onPlayerDemoted;

        /// <summary>
        /// Occurs when a player joins a channel the bot is in.
        /// </summary>
        public event PlayerJoinedChannelEventHandler onPlayerJoinedChannel;

        /// <summary>
        /// Occurs when a player leaves a channel the bot is in.
        /// </summary>
        public event PlayerLeftChannelEventHandler onPlayerLeftChannel;

        /// <summary>
        /// Occurs when a player is promoted in rank of a channel.
        /// </summary>
        public event PlayerPromotedEventHandler onPlayerPromoted;

        /// <summary>
        /// Occurs when two plug-ins attempt to use the same bot flag.
        /// </summary>
        public event PluginConflictEventHandler onPluginConflict;

        /// <summary>
        /// Occurs when the bot sends a message to a channel.
        /// </summary>
        public event SendChannelMessageEventHandler onSendChannelMessage;

        /// <summary>
        /// Occurs when the bot sends a clan invite to a player.
        /// </summary>
        public event SendClanInviteEventHandler onSendClanInvite;

        /// <summary>
        /// Occurs when the bot sends a message to the rest of the clan.
        /// </summary>
        public event SendClanMessageEventHandler onSendClanMessage;

        /// <summary>
        /// Occurs when the bot sends an emote to a channel.
        /// </summary>
        public event SendEmoteEventHandler onSendEmote;

        /// <summary>
        /// Occurs when the bot sends an instant message to a player.
        /// </summary>
        public event SendInstantMessageEventHandler onSendInstantMessage;

        /// <summary>
        /// Occurs when the bot sends a whisper to a player.
        /// </summary>
        public event SendWhisperEventHandler onSendWhisper;

        /// <summary>
        /// Occurs when a player is un-silenced from a channel.
        /// </summary>
        public event SilenceLiftedEventHandler onSilenceLifted;

        /// <summary>
        /// Occurs when a player is silenced from a channel.
        /// </summary>
        public event SilencePlacedEventHandler onSilencePlaced;

        /// <summary>
        /// Occurs when the bot tries to use a slash command.
        /// </summary>
        public event SlashCommandEventHandler onSlashCommand;

        /// <summary>
        /// Occurs when a player, buddy or clan member's channel status changes.
        /// </summary>
        public event StatusUpdateEventHandler onStatusUpdate;

        /// <summary>
        /// Occurs when a channel changes their topic.
        /// </summary>
        public event TopicChangedEventHandler onTopicChanged;

        /// <summary>
        /// Occurs when the bot receives a whisper from a player.
        /// </summary>
        public event WhisperEventHandler onWhisper;

        /// <summary>
        /// Occurs when the chat server returns a whois query.
        /// </summary>
        public event WhoisCompleteEventHandler onWhoisComplete;
        #endregion

        #region Various variables
        private bool IsLoggedIn;
        private string Message;
        private Hashtable Data;

        private char CommPrefix = '!';
        private string Color1 = "^590";
        private string Color2 = "^873";
        private string Color3 = "^444";
        private List<IPlugin> PluginsInstalled = new List<IPlugin>();
        private Dictionary<char, string> BotFlagsUsed = new Dictionary<char, string>();
        private UInt32 CurrentChannelID;
        private string _cookie;
        private UInt32 _userid;
        private MessageType _lastmessagetype;
        private string _lastwhisper;
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the command prefix needed to consider a message a command.
        /// </summary>
        public char CommandPrefix
        {
            get { return CommPrefix; }
            set { CommPrefix = value; }
        }

        /// <summary>
        /// Gets or sets the primary color used in the bot's messages.
        /// </summary>
        public string PrimaryColor
        {
            get { return Color1; }
            set { Color1 = value; }
        }

        /// <summary>
        /// Gets or sets the secondary color used in the bot's messages.
        /// </summary>
        public string SecondaryColor
        {
            get { return Color2; }
            set { Color2 = value; }
        }

        /// <summary>
        /// Gets or sets the tertiary color used in the bot's messages.
        /// </summary>
        public string TertiaryColor
        {
            get { return Color3; }
            set { Color3 = value; }
        }

        /// <summary>
        /// Gets or sets the current channel ID that the bot is focusing on.
        /// </summary>
        public UInt32 ChannelID
        {
            get { return CurrentChannelID; }
            set { CurrentChannelID = value; }
        }

        /// <summary>
        /// Gets the current chat server cookie.
        /// </summary>
        public string Cookie
        {
            get { return this._cookie; }
        }

        /// <summary>
        /// Gets the bot's user ID.
        /// </summary>
        public UInt32 UserID
        {
            get { return this._userid; }
        }

        /// <summary>
        /// Gets the list of plugins currently loaded.
        /// </summary>
        public List<IPlugin> Plugins
        {
            get { return PluginsInstalled; }
        }

        /// <summary>
        /// Gets the last type of message received by the bot.
        /// </summary>
        public MessageType LastMessageType
        {
            get { return this._lastmessagetype; }
        }

        /// <summary>
        /// Gets the username of the last person to whisper the bot.
        /// </summary>
        public string LastWhisper
        {
            get { return this._lastwhisper; }
        }
        #endregion

        #region Enumerators
        public enum FloodState
        {
            FLOOD_STATE_INACTIVE,
            FLOOD_STATE_SLOWED
        }

        public enum MessageType
        {
            BUDDY_WHISPER,
            CLAN_MESSAGE,
            INSTANT_MESSAGE,
            WHISPER
        }

        public enum WhoisType
        {
            USER_INFO_INGAME,
            USER_INFO_NOEXIST,
            USER_INFO_OFFLINE,
            USER_INFO_ONLINE
        }

        public enum UpdateType
        {
            UPDATE_FULL,
            UPDATE_UPDATER,
            UPDATE_FILES,
            UPDATE_NONE
        }
        #endregion

        public Core() { }

        #region Public voids
        /// <summary>
        /// Accepts a given clan invitation.
        /// </summary>
        public void AcceptClanInvitation()
        {
            hon.AcceptClanInvitation();
            if (onAcceptClanInvitation != null) onAcceptClanInvitation(this, new EventArgs());
        }

        /// <summary>
        /// Adds a channel to the auto-join list.
        /// </summary>
        public void AddAutoJoin(string ChannelName)
        {
            string[] args = { _userid.ToString(), ChannelName, _cookie };
            hon.ClientRequester(HoNClient.CLIENT_REQUEST_ADD_ROOM, null, args);
        }

        /// <summary>
        /// Checks for if there is any update available.
        /// </summary>
        /// <returns>Whether or not an update is available.</returns>
        public UpdateType CheckForUpdates()
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlNode remote_manifest;
            XmlNode local_manifest;

            xmlDoc.Load(@"http://dl.dropbox.com/u/2683550/Heroes%20of%20Newerth/NewerthBot/manifest.xml");
            remote_manifest = xmlDoc.SelectSingleNode("/manifest");
            xmlDoc = null;

            xmlDoc = new XmlDocument();
            xmlDoc.Load(AppDomain.CurrentDomain.BaseDirectory + "manifest.xml");
            local_manifest = xmlDoc.SelectSingleNode("/manifest");

            if (new Version(remote_manifest.Attributes[0].Value) > new Version(local_manifest.Attributes[0].Value))
            {
                foreach (XmlNode file in remote_manifest.ChildNodes)
                {
                    if (file.Attributes["path"].Value == "NewerthBot.exe")
                    {
                        foreach (XmlNode local_file in local_manifest.ChildNodes)
                        {
                            if (file.Attributes["checksum"].Value != local_file.Attributes["checksum"].Value)
                            {
                                return UpdateType.UPDATE_FULL;
                            }
                            else
                            {
                                return UpdateType.UPDATE_FILES;
                            }
                        }
                    }
                }
            }
            else
            {
                return UpdateType.UPDATE_NONE;
            }

            return UpdateType.UPDATE_NONE;
        }

        /// <summary>
        /// Declines a given clan invitation.
        /// </summary>
        public void DeclineClanInvitation()
        {
            hon.DeclineClanInvitation();
            if (onDeclineClanInvitation != null) onDeclineClanInvitation(this, new EventArgs());
        }

        /// <summary>
        /// Disconnects the bot from the chat server.
        /// </summary>
        public void Disconnect()
        {
            hon.Disconnect();
            if (onDisconnect != null) onDisconnect(this, new DisconnectEventArgs(""));
        }

        /// <summary>
        /// Gets a player's bot flags using his/her user ID.
        /// </summary>
        /// <param name="UserID">The user ID of the player.</param>
        /// <returns>The player's bot  flags.</returns>
        public List<char> GetBotFlags(UInt32 UserID)
        {
            return UserIDToBotFlags(UserID);
        }

        /// <summary>
        /// Gets a player's bot flags using his/her username.
        /// </summary>
        /// <param name="Username">The username of the player.</param>
        /// <returns>The player's bot flags.</returns>
        public List<char> GetBotFlags(string Username)
        {
            return UsernameToBotFlags(Username);
        }

        /// <summary>
        /// Gets whether or not a player contains a certain bot flag using his/her user ID.
        /// </summary>
        /// <param name="UserID">The user ID of the player.</param>
        /// <param name="BotFlag">The bot flag to check.</param>
        /// <returns>Whether or not the player contains the bot flag.</returns>
        public bool GetBotFlags(UInt32 UserID, char BotFlag)
        {
            List<char> _botflags = UserIDToBotFlags(UserID);

            if (_botflags.Contains(BotFlag) || _botflags.Contains('z'))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Gets whether or not a player contains a certain bot flag using his/her user ID.
        /// </summary>
        /// <param name="Username">The username of the player.</param>
        /// <param name="BotFlag">The bot flag to check.</param>
        /// <returns>Whether or not the player contains the bot flag.</returns>
        public bool GetBotFlags(string Username, char BotFlag)
        {
            List<char> _botflags = UsernameToBotFlags(Username);

            if (_botflags.Contains(BotFlag) || _botflags.Contains('z'))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Gets a player's flags using his/her user ID.
        /// </summary>
        /// <param name="UserID">The user ID of the player.</param>
        /// <returns>The player's flags</returns>
        public byte GetFlags(UInt32 UserID)
        {
            return UserIDToFlags(UserID);
        }

        /// <summary>
        /// Gets a player's flags using his/her username.
        /// </summary>
        /// <param name="Username">The username of the player</param>
        /// <returns>The player's flags</returns>
        public byte GetFlags(string Username)
        {
            return UsernameToFlags(Username);
        }

        /// <summary>
        /// Gets a player's user ID using his/her username.
        /// </summary>
        /// <param name="Username">The username of the player</param>
        /// <returns>The player's user ID</returns>
        public UInt32 GetUserID(string Username)
        {
            return UsernameToUserID(Username);
        }

        /// <summary>
        /// Gets a player's username using his/her user ID.
        /// </summary>
        /// <param name="UserID">The user ID of the player</param>
        /// <returns>The player's username</returns>
        public string GetUsername(UInt32 UserID)
        {
            return UserIDToUsername(UserID);
        }

        /// <summary>
        /// Joins the specified channel.
        /// </summary>
        public void JoinChannel(string ChannelName)
        {
            hon.JoinChannel(ChannelName);
        }

        /// <summary>
        /// Leaves the specified channel.
        /// </summary>
        public void LeaveChannel(string ChannelName)
        {
            hon.PartChannel(ChannelName);
            if (onLeaveChannel != null) onLeaveChannel(this, new LeaveChannelEventArgs(ChannelToChatID(ChannelName), ChannelName));
        }

        /// <summary>
        /// Logs the bot in using the specified credentials.
        /// </summary>
        public void Login(string Username, string Password)
        {
            hon = new HoNClient();
            hon.Login(Username, Password);

            hon.onAuthAccepted += new HoNAuthAcceptedEventHandler(hon_onAuthAccepted);
            hon.onBuddyWhisper += new HoNBuddyWhisperEventHandler(hon_onBuddyWhisper);
            hon.onChangedChannel += new HoNChangedChannelEventHandler(hon_onChangedChannel);
            hon.onChannelDemote += new HoNChannelDemoteEventHandler(hon_onChannelDemote);
            hon.onChannelEmote += new HoNChannelEmoteEventHandler(hon_onChannelEmote);
            hon.onChannelKick += new HoNChannelKickEventHandler(hon_onChannelKick);
            hon.onChannelMessage += new HoNChannelMessageEventHandler(hon_onChannelMessage);
            hon.onChannelPromote += new HoNChannelPromoteEventHandler(hon_onChannelPromote);
            hon.onChannelSilenced += new HoNChannelSilencedEventHandler(hon_onChannelSilenced);
            hon.onChannelSilenceLifted += new HoNChannelSilenceLiftedEventHandler(hon_onChannelSilenceLifted);
            hon.onChannelSilencePlaced += new HoNChannelSilencePlacedEventHandler(hon_onChannelSilencePlaced);
            hon.onChannelTopic += new HoNChannelTopicEventHandler(hon_onChannelTopic);
            hon.onClanInvite += new HoNClanInviteEventHandler(hon_onClanInvite);
            hon.onClanWhisper += new HoNClanWhisperEventHandler(hon_onClanWhisper);
            hon.onHoNLoginInfo += new HoNLoginInfoEventHandler(hon_onHoNLoginInfo);
            hon.onInitialStatus += new HoNInitialStatusEventHandler(hon_onInitialStatus);
            hon.onIM += new HoNIMEventHandler(hon_onIM);
            hon.onJoinedChannel += new HoNJoinedChannelEventHandler(hon_onJoinedChannel);
            hon.onLeftChannel += new HoNLeftChannelEventHandler(hon_onLeftChannel);
            hon.onLogin += new HoNLoginEventHandler(hon_onLogin);
            hon.onPing += new HoNPingEventHandler(hon_onPing);
            hon.onRollDice += new HoNRollDiceEventHandler(hon_onRollDice);
            hon.onSocketConnected += new SocketConnectedHandler(hon_onSocketConnected);
            hon.onSocketError += new SocketErroredHandler(hon_onSocketError);
            hon.onUpdateStatus += new HoNUpdateStatusEventHandler(hon_onUpdateStatus);
            hon.onUserInfoInGame += new HoNUserInfoInGameEventHandler(hon_onUserInfoInGame);
            hon.onUserInfoNoExist += new HoNUserInfoNoExistEventHandler(hon_onUserInfoNoExist);
            hon.onUserInfoOffline += new HoNUserInfoOfflineEventHandler(hon_onUserInfoOffline);
            hon.onUserInfoOnline += new HoNUserInfoOnlineEventHandler(hon_onUserInfoOnline);
            hon.onWhisper += new HoNWhisperEventHandler(hon_onWhisper);
            hon.onNameChange += new HoNNameChangeEventHandler(hon_onNameChange);
        }

        /// <summary>
        /// Removes a channel from the auto-join list.
        /// </summary>
        public void RemoveAutoJoin(string ChannelName)
        {
            string[] args = { _userid.ToString(), ChannelName, _cookie };
            hon.ClientRequester(HoNClient.CLIENT_REQUEST_REMOVE_ROOM, null, args);
        }

        /// <summary>
        /// Makes the core scan for any admins.
        /// </summary>
        public void ScanAdmins()
        {
            List<char> botflags;
            XmlDocument xmlDoc = new XmlDocument();
            XmlNodeList xmlAdmins;
            xmlDoc.Load(AppDomain.CurrentDomain.BaseDirectory + "newerthbot.xml");
            xmlAdmins = xmlDoc.SelectNodes("/newerthbot/users/user");

            foreach (XmlNode admin in xmlAdmins)
            {
                botflags = new List<char>();
                botflags.AddRange(admin.Attributes[0].Value.ToCharArray());

                if (GetBotFlags(admin.InnerText) != null)
                {
                    if (GetBotFlags(admin.InnerText).Count == 0 && admin.Attributes[0].Value.Length != 0)
                    {
                        UpdateBotFlags(admin.InnerText, botflags);                        
                    }
                }
                else
                {
                    hemUsers.AddEntity(admin.InnerText, UInt32.Parse(admin.Attributes[1].Value), 0, botflags);
                }
            }
        }

        /// <summary>
        /// Makes the core scan for any plug-ins.
        /// </summary>
        public void ScanPlugins()
        {
            string SelfDir = AppDomain.CurrentDomain.BaseDirectory;
            bool found = false;
            char flagconflict = ' ';

            foreach (string directory in Directory.GetDirectories(SelfDir + "Plugins"))
            {
                foreach (string file in Directory.GetFiles(directory))
                {
                    FileInfo fileinfo = new FileInfo(file);

                    if (fileinfo.Extension == ".dll")
                    {
                        Assembly pluginAssembly = Assembly.LoadFrom(file);

                        foreach (Type pluginType in pluginAssembly.GetTypes())
                        {
                            if (pluginType.IsPublic && !pluginType.IsAbstract)
                            {
                                Type typeInterface = pluginType.GetInterface("LibBot.IPlugin", true);

                                if (typeInterface != null)
                                {
                                    IPlugin newPlugin = (IPlugin)Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()));

                                    foreach (char flag in newPlugin.BotFlags)
                                    {
                                        if (BotFlagsUsed.ContainsKey(flag))
                                        {
                                            flagconflict = flag;
                                            found = true;
                                        }
                                    }

                                    if (!found)
                                    {
                                        foreach (char flag in newPlugin.BotFlags)
                                        {
                                            BotFlagsUsed.Add(flag, newPlugin.Name);
                                        }

                                        newPlugin.Initialize(this);
                                        PluginsInstalled.Add(newPlugin);
                                    }
                                    else
                                    {
                                        if (onPluginConflict != null) onPluginConflict(this, new PluginConflictEventArgs(BotFlagsUsed[flagconflict], newPlugin.Name, flagconflict));
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Sends a message to the specified channel ID.
        /// </summary>
        public void SendChannelMessage(UInt32 ChannelID, string Message)
        {
            string Command;
            string Parameters;

            Message = string.Format(Message, Color1, Color2, Color3);

            if (Message.StartsWith("/"))
            {
                if (Message.Contains(" "))
                {
                    Command = Message.Substring(1, Message.IndexOf(' ') - 1).ToLower();
                    Parameters = Message.Substring(Message.IndexOf(' ') + 1);
                }
                else
                {
                    Command = Message.Substring(1);
                    Parameters = "";
                }

                if (onSlashCommand != null) onSlashCommand(this, new SlashCommandEventArgs(Command, Parameters));
            }
            else
            {
                hon.SendChannelMessage(ChannelID, Message);
                if (onSendChannelMessage != null) onSendChannelMessage(this, new SendChannelMessageEventArgs(ChannelID, Message));
            }
        }

        /// <summary>
        /// Sends a message to the rest of the clan.
        /// </summary>
        public void SendClanMessage(string Message)
        {
            hon.SendClanMessage(Message);
            if (onSendClanMessage != null) onSendClanMessage(this, new SendClanMessageEventArgs(Message));
        }

        /// <summary>
        /// Sends an emote to the specified channel ID.
        /// </summary>
        public void SendEmote(UInt32 ChannelID, string Message)
        {
            hon.SendChannelEmote(ChannelID, Message);
            if (onSendEmote != null) onSendEmote(this, new SendEmoteEventArgs(ChannelID, Message));
        }

        /// <summary>
        /// Sends an instant message to the specified username.
        /// </summary>
        public void SendInstantMessage(string Username, string Message)
        {
            hon.SendIM(Username, Message);
            if (onSendInstantMessage != null) onSendInstantMessage(this, new SendInstantMessageEventArgs(Username, Message));
        }

        /// <summary>
        /// Sends a whisper to the specified username.
        /// </summary>
        public void SendWhisper(string Username, string Message)
        {
            hon.SendWhisper(Username, Message);
            if (onSendWhisper != null) onSendWhisper(this, new SendWhisperEventArgs(Username, Message));
        }

        /// <summary>
        /// Sends a clan invitation to the specified username.
        /// </summary>
        public void SendClanInvite(string Username)
        {
            hon.SendClanInvite(Username);
            if (onSendClanInvite != null) onSendClanInvite(this, new SendClanInviteEventArgs(Username));
        }

        /// <summary>
        /// Queries a whois of the specified username to the chat server.
        /// </summary>
        public void Whois(string Username)
        {
            hon.SendWhois(Username);
        }
        #endregion

        #region Private voids
        private UInt32 UsernameToUserID(string Username)
        {
            return hemUsers.GetEntityIDFromEntity(Username);
        }
        private string UserIDToUsername(UInt32 UserID)
        {
            return hemUsers.GetEntityFromEntityID(UserID);
        }
        private byte UsernameToFlags(string Username)
        {
            return hemUsers.GetFlagsFromEntity(Username);
        }
        private byte UserIDToFlags(UInt32 UserID)
        {
            return hemUsers.GetFlagsFromEntityID(UserID);
        }
        private List<char> UsernameToBotFlags(string Username)
        {
            return hemUsers.GetBotFlagsFromEntity(Username);
        }
        private List<char> UserIDToBotFlags(UInt32 UserID)
        {
            return hemUsers.GetBotFlagsFromEntityID(UserID);
        }
        private UInt32 ChannelToChatID(string Channel)
        {
            return hemChannels.GetEntityIDFromEntity(Channel);
        }
        private string ChatIDToChannel(UInt32 ChatID)
        {
            return hemChannels.GetEntityFromEntityID(ChatID);
        }
        private void UpdateFlags(string Username, byte Flags)
        {
            hemUsers.UpdateFlags(Username, Flags);
        }
        private void UpdateFlags(UInt32 UserID, byte Flags)
        {
            hemUsers.UpdateFlags(UserID, Flags);
        }
        private void UpdateBotFlags(string Username, List<char> BotFlags)
        {
            hemUsers.AddBotFlags(Username, BotFlags);
        }
        private void UpdateBotFlags(UInt32 UserID, List<char> BotFlags)
        {
            hemUsers.AddBotFlags(UserID, BotFlags);
        }
        #endregion

        #region Eventhandling
        void hon_onAuthAccepted(object sender, HoNAuthAcceptedArgs e)
        {
            if (onLoginComplete != null) onLoginComplete(this, new LoginCompleteEventArgs(IsLoggedIn, Message, Data));
        }

        void hon_onBuddyWhisper(object sender, HoNBuddyWhisperArgs e)
        {
            string Username = e.GetUsername();
            string Message = e.GetMessage();
            _lastmessagetype = MessageType.BUDDY_WHISPER;

            if (onBuddyWhisper != null) onBuddyWhisper(this, new BuddyWhisperEventArgs(Username, Message));
        }

        void hon_onChangedChannel(object sender, HoNChangedChannelArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = e.GetChannel();
            string Topic = e.GetTopic();
            HoNOper[] Operators = e.GetOpers();
            HoNUser[] Users = e.GetUsers();
            UInt32 UserCount = e.GetNumberOfUsers();

            foreach (HoNUser user in Users)
            {
                hemUsers.AddEntity(user.Username, user.UserID, user.Flags, new List<char> { });
                hemUsers.UpdateFlags(user.Username, user.Flags);
            }

            hemChannels.AddEntity(ChannelName, ChannelID, 0, new List<char> { });

            if (onNewChannel != null) onNewChannel(this, new NewChannelEventArgs(ChannelID, ChannelName, Topic, Operators, Users, UserCount));
        }

        void hon_onChannelDemote(object sender, HoNChannelDemoteArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = ChatIDToChannel(ChannelID);
            UInt32 OperatorID = e.GetOperID();
            string OperatorName = UserIDToUsername(OperatorID);
            UInt32 TargetID = e.GetTargetID();
            string TargetName = UserIDToUsername(TargetID);

            if (onPlayerDemoted != null) onPlayerDemoted(this, new PlayerDemotedEventArgs(ChannelID, ChannelName, OperatorID, OperatorName, TargetID, TargetName));
        }

        void hon_onChannelEmote(object sender, HoNChannelEmoteArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = ChatIDToChannel(ChannelID);
            UInt32 UserID = e.GetUserID();
            string Username = UserIDToUsername(UserID);
            string Message = e.GetMessage();

            if (onEmote != null) onEmote(this, new EmoteEventArgs(ChannelID, ChannelName, UserID, Username, Message));
        }

        void hon_onChannelKick(object sender, HoNChannelKickArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = ChatIDToChannel(ChannelID);
            UInt32 KickerID = e.GetKickerID();
            string KickerName = UserIDToUsername(KickerID);
            UInt32 KickedID = e.GetKickedID();
            string KickedName = UserIDToUsername(KickedID);

            if (onChannelKick != null) onChannelKick(this, new ChannelKickEventArgs(ChannelID, ChannelName, KickerID, KickerName, KickedID, KickedName));
        }

        void hon_onChannelMessage(object sender, HoNChannelMessageArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = ChatIDToChannel(ChannelID);
            UInt32 UserID = e.GetUserID();
            string Username = UserIDToUsername(UserID);
            List<char> BotFlags = UserIDToBotFlags(UserID);
            string Message = e.GetMessage();

            if (onChannelMessage != null) onChannelMessage(this, new ChannelMessageEventArgs(ChannelID, ChannelName, UserID, Username, Message));

            if (Message.StartsWith(CommPrefix.ToString()))
            {
                string command;
                string parameters;

                if (Message.Contains(" "))
                {
                    command = Message.Substring(1, Message.IndexOf(' ') - 1).ToLower();
                    parameters = Message.Substring(Message.IndexOf(' ') + 1);
                }
                else
                {
                    command = Message.Substring(1);
                    parameters = "";
                }

                if (onCommand != null) onCommand(this, new CommandEventArgs(ChannelID, ChannelName, UserID, Username, BotFlags, command, parameters));
            }
        }

        void hon_onChannelPromote(object sender, HoNChannelPromoteArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = ChatIDToChannel(ChannelID);
            UInt32 OperatorID = e.GetOperID();
            string OperatorName = UserIDToUsername(OperatorID);
            UInt32 TargetID = e.GetTargetID();
            string TargetName = UserIDToUsername(TargetID);

            if (onPlayerPromoted != null) onPlayerPromoted(this, new PlayerPromotedEventArgs(ChannelID, ChannelName, OperatorID, OperatorName, TargetID, TargetName));
        }

        void hon_onChannelSilenced(object sender, HoNChannelSilencedArgs e)
        {

        }

        void hon_onChannelSilenceLifted(object sender, HoNChannelSilenceLiftedArgs e)
        {
            string ChannelName = e.GetChannel();
            UInt32 ChannelID = ChannelToChatID(ChannelName);

            if (onSilenceLifted != null) onSilenceLifted(this, new SilenceLiftedEventArgs(ChannelID, ChannelName));
        }

        void hon_onChannelSilencePlaced(object sender, HoNChannelSilencePlacedArgs e)
        {
            string ChannelName = e.GetChannel();
            UInt32 ChannelID = ChannelToChatID(ChannelName);
            string MuterName = e.GetMuter();
            UInt32 MuterID = UsernameToUserID(MuterName);
            string MutedName = e.GetMuted();
            UInt32 MutedID = UsernameToUserID(MutedName);
            int Duration = (int)e.GetDuration();

            if (onSilencePlaced != null) onSilencePlaced(this, new SilencePlacedEventArgs(ChannelID, ChannelName, MuterID, MuterName, MutedID, MutedName, Duration));
        }

        void hon_onChannelTopic(object sender, HoNChannelTopicArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = ChatIDToChannel(ChannelID);
            string Topic = e.GetTopic();

            if (onTopicChanged != null) onTopicChanged(this, new TopicChangedEventArgs(ChannelID, ChannelName, Topic));
        }

        void hon_onClanInvite(object sender, HoNClanInviteArgs e)
        {
            string ClanName = e.GetClan();
            string Username = e.GetUsername();
            UInt32 UserID = UsernameToUserID(Username);

            if (onClanInvite != null) onClanInvite(this, new ClanInvitationEventArgs(ClanName, UserID, Username));
        }

        void hon_onClanWhisper(object sender, HoNClanWhisperArgs e)
        {
            UInt32 UserID = e.GetUserID();
            string Username = UserIDToUsername(UserID);
            string Message = e.GetMessage();
            _lastmessagetype = MessageType.CLAN_MESSAGE;

            if (onClanMessage != null) onClanMessage(this, new ClanMessageEventArgs(UserID, Username, Message));
        }

        void hon_onHoNLoginInfo(object sender, HoNLoginInfoArgs e)
        {
            Data = e.Data();
            _userid = uint.Parse((string)Data["account_id"]);
            _cookie = (string)Data["cookie"];

            if (Data["auth"] != null)
            {
                if ((string)Data["auth"] == "Invalid Nickname or Password.")
                {
                    return;
                }
            }

            if (((Hashtable)Data["buddy_list"]) != null)
            //if ((((Hashtable)Data["buddy_list"])[int.Parse((string)Data["account_id"])]).GetType().Name == "ArrayList")
            {
                ArrayList _buddylist = new ArrayList();
                _buddylist = (ArrayList)((Hashtable)Data["buddy_list"])[int.Parse((string)Data["account_id"])];

                foreach (Hashtable buddy in _buddylist)
                {
                    hemUsers.AddEntity((string)buddy["nickname"], uint.Parse((string)buddy["buddy_id"]), 0, new List<char> { });
                }
            }

            if (((Hashtable)Data["clan_roster"])["error"] == null)
            {
                Hashtable _clanroster = new Hashtable();
                _clanroster = (Hashtable)Data["clan_roster"];

                foreach (Hashtable member in _clanroster.Values)
                {
                    hemUsers.AddEntity((string)member["nickname"], uint.Parse((string)member["account_id"]), 0, new List<char> { });
                }
            }
        }

        void hon_onJoinedChannel(object sender, HoNJoinedChannelArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = ChatIDToChannel(ChannelID);
            HoNUser User = e.GetUser();

            hemUsers.AddEntity(User.Username, User.UserID, User.Flags, new List<char> { });
            hemUsers.UpdateFlags(User.Username, User.Flags);

            if (onPlayerJoinedChannel != null) onPlayerJoinedChannel(this, new PlayerJoinedChannelEventArgs(ChannelID, ChannelName, User));
        }

        void hon_onLeftChannel(object sender, HoNLeftChannelArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = ChatIDToChannel(ChannelID);
            UInt32 UserID = e.GetUserID();
            string Username = UserIDToUsername(UserID);

            if (onPlayerLeftChannel != null) onPlayerLeftChannel(this, new PlayerLeftChannelEventArgs(ChannelID, ChannelName, UserID, Username));
        }

        void hon_onInitialStatus(object sender, HoNInitialStatusArgs e)
        {
            HoNBuddy[] Buddies = e.GetBuddies();

            foreach (HoNBuddy buddy in Buddies)
            {
                UpdateFlags(buddy.UserID, buddy.Flags);
            }

            if (onInitialStatus != null) onInitialStatus(this, new InitialStatusEventArgs(Buddies));
        }

        void hon_onIM(object sender, HoNIMArgs e)
        {
            string Username = e.GetUsername();
            string Message = e.GetMessage();

            if (onInstantMessage != null) onInstantMessage(this, new InstantMessageEventArgs(Username, Message));
        }

        void hon_onLogin(object sender, HoNLoginResultArgs e)
        {
            IsLoggedIn = e.IsLoggedIn();
            Message = e.GetMessage();

            if (!IsLoggedIn)
            {
                if (onLoginComplete != null) onLoginComplete(this, new LoginCompleteEventArgs(IsLoggedIn, Message, Data));
            }
        }

        void hon_onNameChange(object sender, HoNNameChangeArgs e)
        {
            UInt32 UserID = e.GetUserID();
            string Username = e.GetUsername();

            if (onNameChanged != null) onNameChanged(this, new NameChangedEventArgs(UserID, Username));
        }

        void hon_onPing(object sender, HoNPingArgs e)
        {
            hon.SendPong();
        }

        void hon_onRollDice(object sender, HoNRollDiceArgs e)
        {
            UInt32 ChannelID = e.GetChatID();
            string ChannelName = ChatIDToChannel(ChannelID);
            UInt32 UserID = e.GetUserID();
            string Username = UserIDToUsername(UserID);
            string Message = e.GetMessage();

            if (onDiceRoll != null) onDiceRoll(this, new DiceRollEventArgs(ChannelID, ChannelName, UserID, Username, Message));
        }

        void hon_onSocketConnected(object sender, SocketConnectEventArgs e)
        {
            if (onConnect != null) onConnect(this, new EventArgs());
        }

        void hon_onSocketError(object sender, SocketErrorEventArgs e)
        {
            if (onDisconnect != null) onDisconnect(this, new DisconnectEventArgs(e.GetErrorText()));
        }

        void hon_onUpdateStatus(object sender, HoNUpdateStatusArgs e)
        {
            UInt32 UserID = e.GetUserID();
            string Username = UserIDToUsername(UserID);
            byte Status = e.GetStatus();
            byte Flags = e.GetFlags();
            UInt32 ClanID = e.GetClanID();
            string Clan = e.GetClan();
            string GameServer = e.GetGameServer();
            UInt32 GameID = e.GetGameID();
            string GameName = e.GetGameName();

            if (onStatusUpdate != null) onStatusUpdate(this, new StatusUpdateEventArgs(UserID, Username, Status, Flags, ClanID, Clan, GameServer, GameID, GameName));
        }

        void hon_onUserInfoInGame(object sender, HoNUserInfoInGameArgs e)
        {
            WhoisType Type = WhoisType.USER_INFO_INGAME;
            if (onWhoisComplete != null) onWhoisComplete(this, new WhoisCompleteEventArgs(Type, e, null, null, null));
        }

        void hon_onUserInfoNoExist(object sender, HoNUserInfoNoExistArgs e)
        {
            WhoisType Type = WhoisType.USER_INFO_NOEXIST;
            if (onWhoisComplete != null) onWhoisComplete(this, new WhoisCompleteEventArgs(Type, null, e, null, null));
        }

        void hon_onUserInfoOffline(object sender, HoNUserInfoOfflineArgs e)
        {
            WhoisType Type = WhoisType.USER_INFO_OFFLINE;
            if (onWhoisComplete != null) onWhoisComplete(this, new WhoisCompleteEventArgs(Type, null, null, e, null));
        }

        void hon_onUserInfoOnline(object sender, HoNUserInfoOnlineArgs e)
        {
            WhoisType Type = WhoisType.USER_INFO_ONLINE;
            if (onWhoisComplete != null) onWhoisComplete(this, new WhoisCompleteEventArgs(Type, null, null, null, e));
        }

        void hon_onWhisper(object sender, HoNWhisperArgs e)
        {
            string Username = e.GetUsername();
            string Message = e.GetMessage();
            List<char> BotFlags = UsernameToBotFlags(Username);
            _lastmessagetype = MessageType.WHISPER;
            _lastwhisper = Username;

            if (Message.StartsWith(CommPrefix.ToString()))
            {
                string command = Message.Substring(1, Message.IndexOf(' ') - 1).ToLower();
                string parameters = Message.Substring(Message.IndexOf(' ') + 1);

                if (onCommand != null) onCommand(this, new CommandEventArgs(0, null, 0, Username, BotFlags, command, parameters));
            }

            if (onWhisper != null) onWhisper(this, new WhisperEventArgs(Username, Message));
        }
        #endregion
    }
}
